/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 ("License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.zimbra.com/license
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is: Zimbra AJAX Toolkit.
*
* The Initial Developer of the Original Code is Zimbra, Inc.
* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
* All Rights Reserved.
*
* Portions of the original code have been adapted for CLDC 1.0 and MIDP 2.0 by CalemEAM Inc. 
* Portions created by CalemEAM are Copyright (C) 2008 CalemEAM Inc. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK *****
*/
package com.calemeam.text;

//

import java.util.Calendar;
import java.util.Date;
import java.util.Vector;
//

/**
 * The AjxDateFormat class formats Date objects according to a specified 
 * pattern. The patterns are defined the same as the SimpleDateFormat
 * class in the Java libraries. <strong>Note:</strong> <em>Only the
 * Gregorian Calendar is supported at this time.</em> Supporting other
 * calendars would require a lot more information downloaded to the
 * client. Limiting dates to the Gregorian calendar is a trade-off.
 * <p>
 * <strong>Note:</strong>
 * The date format differs from the Java patterns a few ways: the pattern
 * "EEEEE" (5 'E's) denotes a <em>short</em> weekday and the pattern "MMMMM"
 * (5 'M's) denotes a <em>short</em> month name. This matches the extended 
 * pattern found in the Common Locale Data Repository (CLDR) found at: 
 * http://www.unicode.org/cldr/.
 */

public class MicroDateFormat extends MicroFormat {
   /**
    * Format descriptors excluding Era, week, Day in year,  DOW in month (F), timezone support.
    */
   public static final String META_CHARS = "yMdEaHhms";
                                         //"GyMwWDdFEaHkKhmsSzZ"  -- original
   
   /**
    * Two digit year base for figuring out 4 digit year.
    */
   public static final int dateParsing2DigitStartYear=1936;
   /**
    * Only support medium month and day
    */
   public static Vector MONTH_MEDIUM; 
   /**
    * Only support medium day of the week
    */
   public static Vector DOW_MEDIUM;   
   
   //Static initializers
   public static synchronized void initRes() {
      MONTH_MEDIUM=MicroFormatUtil.resFromArray(MicroResIf.AR_MM);
      DOW_MEDIUM=MicroFormatUtil.resFromArray(MicroResIf.DOW_M);
   }
   
   /**
    * The pattern is a string of date format.
    * <ul>
    * <li>y year
    * <li>M Month
    * <li>d Day of month
    * <li>E Day of week
    * <li>a AM/PM
    * <li>H 24-hour
    * <li>h 12-hour
    * <li>m Minutes
    * <li>s Seconds
    * </ul>
    */
   public MicroDateFormat(String pattern) {
      super(pattern);
      for (int i = 0; i < pattern.length(); i++) {
         //Removed literal support for micro edition.
         char c=pattern.charAt(i);
         // non-meta chars
         int head = i;
         while(i < pattern.length()) {
            c = pattern.charAt(i);
            if (META_CHARS.indexOf(c) != -1) {
               break;
            }
            i++;
         }
         int tail = i;
         if (head != tail) {
            MicroTextSegment segment = new MicroTextSegment(this, pattern.substring(head, tail));
            segment.setIdx(this.segments.size());
            this.segments.addElement(segment);
            i--;
            continue;
         }

         // meta char
         head = i;
         while(++i < pattern.length()) {
            if (pattern.charAt(i) != c) {
               break;
            }		
         }
         tail = i--;
         String field = pattern.substring(head, tail);
         MicroSegment segment = null;
         switch (c) {
            case 'y': segment = new YearSegment(this, field); break;
            case 'M': segment = new MonthSegment(this, field); break;
            case 'd': segment = new DaySegment(this, field); break;
            case 'E': segment = new WeekdaySegment(this, field); break;
            case 'a': segment = new AmPmSegment(this, field); break;
            case 'H': segment = new HourSegment(this, field); break;
            case 'h': segment = new HourSegment(this, field); break;
            case 'm': segment = new MinuteSegment(this, field); break;
            case 's': segment = new SecondSegment(this, field); break;
         }
         if (segment != null) {
            segment.setIdx(this.segments.size());
            this.segments.addElement(segment);
         }
      }
   }
//#if Debug
//#    public String toString() {
//#       return "[MicroDateFormat: "+super.toString()+"]";
//#    };   
//#endif   
   
   /**
    * Get format instance based on format string.
    */
   public static MicroDateFormat getInstance(String format) {
      MicroDateFormat mdf=(MicroDateFormat)FORMATTERS.get(format);
      if (mdf==null) {
         mdf=new MicroDateFormat(format);
         FORMATTERS.put(format, mdf);
      }
      return mdf;
   }
   
   /**
    * format date, datetime, time
    */
   public String format(Date date) {
      return getInstance(pattern)._format(date);
   }
   
   /**
    * parse date, datetime, time
    */
   //#if ParsingConfiguration
//#    public Date parse(String pattern, String dateStr) throws MicroParsingException {
//#       return getInstance(pattern).parse(dateStr);
//#    }
   //#endif

   // Public methods

   /** 
    * Parses the given string and returns a date. If the string cannot be
    * parsed as a date, <code>null</code> is returned.
    */
   //#if ParsingConfiguration
//#    public Date parse(String s) throws MicroParsingException {
//#       Calendar cal=(Calendar)super.parseObj(s);
//#       try {
//#          return cal.getTime();
//#       } catch (IllegalArgumentException iae) {
//#          throw new MicroParsingException("Illegal date: "+iae.getMessage());
//#       }
//#    };
   //#endif

   // Protected methods
   //#if ParsingConfiguration
//#    public Object createParseObject() {
//#       return MicroFormatUtil.getBlankCalendar();      
//#    }
   //#endif

   //
   // Date segment class
   //
   protected abstract class DateSegment extends MicroSegment {
      public DateSegment(MicroFormat format, String seg) {
         super(format, seg);
      }

      // Protected methods
      protected int getFixedLength() {
         int fixedlen=0;
         if (this.getIdx() + 1 < this.format.getSegments().size()) {
            MicroSegment nextSegment = this.format.getSegment(this.getIdx()+1);
            if (!(nextSegment instanceof MicroTextSegment)) {
               fixedlen = this.seg.length();
            }
         }
         return fixedlen;
      };
   }

   //
   // Date year segment class
   //
   public class YearSegment extends DateSegment {
      public YearSegment(MicroFormat format, String seg) {
         super(format, seg);
      }
      public String toString() {
         return "dateYear: "+this.seg;
      }

      // Public methods
      public String format(Object ob) { 
         String year=MicroFormatUtil.getDateSeg(ob, Calendar.YEAR);
         return this.seg.length() < 4 ? year.substring(year.length() - 2) : MicroFormat.zeroPad(year, this.seg);
      }
      
      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {
//#          Calendar cal=(Calendar)ob;
//#          int fixedlen = this.getFixedLength();
//#          int nindex = MicroSegment.parseInt(cal, Calendar.YEAR, 0, s, index, fixedlen);
//#          // adjust 2-digit years
//#          if (nindex - index == 2) {
//#             int syear = dateParsing2DigitStartYear;
//#             int pyear = Integer.parseInt(s.substring(index,index+2));
//#             int century = (syear / 100 + (pyear < (syear % 100) ? 1 : 0)) * 100;
//#             cal.set(Calendar.YEAR, century + pyear);
//#          }
//#          return nindex;
//#       }
      //#endif
   }

   //
   // Date month segment class
   //
   public class MonthSegment extends DateSegment {
      public MonthSegment(MicroFormat format, String seg) {
          super(format, seg);
          /**
           * Init month medium on demand so that the MicroResIf is set before use.
           */
          if (MONTH_MEDIUM==null) initRes();
      }
      //#if Debug
//#       public String toString() { 
//#          return "dateMonth:"+this.seg; 
//#       };
      //#endif

      // Public methods
      public String format(Object ob) {
         int month=MicroFormatUtil.getDateSegInt(ob, Calendar.MONTH);
         switch (this.seg.length()) {
            case 1: return Integer.toString(month+1);
            case 2: return MicroFormat.zeroPad(month + 1, 2);
            default: return (String)MONTH_MEDIUM.elementAt(month);
         }
      };

      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {
//#          if (this.seg.length()>2) {
//#             throw new MicroParsingException("Medium month parsing not supported"); // I18n
//#          }
//#          int fixedlen = this.getFixedLength();
//#          return MicroSegment.parseInt(ob, Calendar.MONTH, -1, s, index, fixedlen);
//#       };
      //#endif
   }

   //
   // Date day segment class
   //
   protected class DaySegment extends DateSegment {
      public DaySegment(MicroFormat format, String seg) {
          super(format, seg);
      }
      //#if Debug
//#       public String toString() { 
//#          return "DaySegment:"+this.seg; 
//#       };
      //#endif
      // Public methods
      public String format(Object ob) {
         String day=MicroFormatUtil.getDateSeg(ob, Calendar.DAY_OF_MONTH);
         return MicroFormat.zeroPad(day, this.seg);
      };

      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {
//#          int fixedlen = this.getFixedLength();
//#          return MicroSegment.parseInt(ob, Calendar.DAY_OF_MONTH, 0, s, index, fixedlen);
//#       };
      //#endif
   }
   //
   // Date weekday segment class
   //
   protected class WeekdaySegment extends DateSegment {
      public WeekdaySegment(MicroFormat format, String seg) {
          super(format, seg);
          if (DOW_MEDIUM==null) initRes();
      }
      //#if Debug
//#       public String toString() { 
//#          return "WeekdaySegment:"+this.seg; 
//#       };
      //#endif
      // Public methods
      public String format(Object ob) {
         int weekday=MicroFormatUtil.getDateSegInt(ob, Calendar.DAY_OF_WEEK);
         if (this.seg.length()>2) {
            return (String)DOW_MEDIUM.elementAt(weekday-1);
         }
         return MicroFormat.zeroPad(weekday, this.seg);
      };
   }
   //
   // Time hour segment class
   //
   protected class HourSegment  extends MicroSegment {
      public HourSegment(MicroFormat format, String seg) {
          super(format, seg);
      }
      //#if Debug
//#       public String toString() { 
//#          return "HourSegment:"+this.seg; 
//#       };
      //#endif
      // Public methods
      public String format(Object ob) {
         int hours = MicroFormatUtil.getDateSegInt(ob, Calendar.HOUR_OF_DAY);
         if (hours > 12 && this.seg.indexOf("h")!=-1) {
            hours -= 12;
         }
         return MicroFormat.zeroPad(hours, this.seg);
      }
      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {	
//#          return MicroSegment.parseIntOpen(ob, Calendar.HOUR_OF_DAY, 0, s, index);
//#       }    
      //#endif
   }
   //
   // Time minute segment class
   //
   protected class MinuteSegment  extends MicroSegment {
      public MinuteSegment(MicroFormat format, String seg) {
          super(format, seg);
      }
      //#if Debug
//#       public String toString() { 
//#          return "MinuteSegment:"+this.seg; 
//#       };
      //#endif
      // Public methods
      public String format(Object ob) {
         int min = MicroFormatUtil.getDateSegInt(ob, Calendar.MINUTE);
         return MicroFormat.zeroPad(min, this.seg);
      }
      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {	
//#          return MicroSegment.parseIntOpen(ob, Calendar.MINUTE, 0, s, index);
//#       }
      //#endif
   }

   //
   // Time second segment class
   //
   protected class SecondSegment  extends MicroSegment {
      public SecondSegment(MicroFormat format, String seg) {
          super(format, seg);
      }
      //#if Debug
//#       public String toString() { 
//#          return "SecondSegment:"+this.seg; 
//#       };
      //#endif
      // Public methods
      public String format(Object ob) {
         int min = MicroFormatUtil.getDateSegInt(ob, Calendar.SECOND);
         return MicroFormat.zeroPad(min, this.seg);
      }
      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {	
//#          return MicroSegment.parseIntOpen(ob, Calendar.SECOND, 0, s, index);
//#       }
      //#endif
   }
   //
   // Time am/pm segment class
   //
   protected class AmPmSegment  extends MicroSegment {
      public AmPmSegment(MicroFormat format, String seg) {
          super(format, seg);
      }
      //#if Debug
//#       public String toString() { 
//#          return "AmPmSegment:"+this.seg; 
//#       };
      //#endif
      // Public methods
      public String format(Object ob) {
         int hours = MicroFormatUtil.getDateSegInt(ob, Calendar.HOUR_OF_DAY);
         String key= hours < 12 ? MicroResIf.am_key : MicroResIf.pm_key;
         return MicroFactory.getRes().get(key);
      };
    
      //#if ParsingConfiguration
//#       public int parse(Object ob, String s, int index) throws MicroParsingException {	
//#          String ap=null;
//#          int nindex=index;
//#          try {
//#             nindex=MicroSegment.parseLiteralOpen(s, index, ' ');
//#             ap=s.substring(index, nindex);
//#          } catch (Exception e) {}
//#          if (ap!=null) {
//#             //Only do PM adjustment.
//#             if (ap.equals(MicroFactory.getRes().get(MicroResIf.pm_key))) {
//#                Calendar cal=(Calendar)ob;
//#                int hours = cal.get(Calendar.HOUR_OF_DAY);
//#                if (hours < 12) {
//#                   hours+=12;
//#                   cal.set(Calendar.HOUR_OF_DAY, hours);
//#                }
//#             }
//#          }
//#          return nindex;
//#       }
      //#endif
   }
}
